﻿using King.Learn.DoNet.CSharp11.Example;
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Runtime.CompilerServices;

namespace King.Learn.DoNet.CSharp11
{
    public class Program
    {
        public static void Main(string[] args)
        {
            try
            {
                #region 1. 抽象和静态方法
                {
                    int result = Bar.Foo1();
                    Console.WriteLine(result);

                    // 由于运算符也属于静态方法，因此从C# 11开始，也可以用接口来对运算符进行抽象了
                    var p1 = new Point(1, 2);
                    var p2 = new Point(2, 3);
                    Console.WriteLine(p1 + p2);

                    // 调用显式实现方法时，需要使用泛型限制
                    Console.WriteLine(Add(p1, p2));

                    T Add<T>(T left, T right) where T : ICanAdd<T>
                    {
                        return left + right;
                    }
                }

                {
                    // 对于不是运算符的情况，则可以利用泛型参数来调用接口上的抽象和静态方法：
                    // 以下调用会调用隐式实现
                    Bar.Foo1();

                    // 以下调用会调用显式实现
                    CallFoo1<Bar>();
                    // 内部方法，C# 10新特性
                    void CallFoo1<T>() where T : IFoo
                    {
                        Console.WriteLine(T.Foo1());
                    }
                }

                {
                    // 此外，接口可以基于另一个接口扩展，
                    // 因此对于静态抽象和静态虚方法而言，我们可以利用这个特性在接口上实现多态

                    CallFoo<Bar1>();
                    CallFoo<Bar2>();
                    CallFoo<Bar3>();
                    CallFooFromIA<Bar4>();
                    CallFooFromIB<Bar4>();

                    void CallFoo<T>() where T : IC
                    {
                        CallFooFromIA<T>();
                        CallFooFromIB<T>();
                    }

                    void CallFooFromIA<T>() where T : IA
                    {
                        Console.WriteLine(T.Foo());
                    }

                    void CallFooFromIB<T>() where T : IB
                    {
                        Console.WriteLine(T.Foo());
                    }
                }

                #endregion 1. 抽象和静态方法

                #region 2. 泛型Attribute

                {
                    // C# 11正式允许用户编写和使用泛型Attribute，因此我们可以不再需要使用Type来在Attribute中存储类型信息，
                    // 这不仅支持了类型推导，还允许用户通过泛型约束在编译时就能对类型进行限制
                    [Foo<int>(3)]
                    [Foo<float>(4.5f)]
                    // [Foo<string>("test")] // error
                    void MyFancyMethod()
                    {
                    }

                    MyFancyMethod();
                }

                #endregion 2. 泛型Attribute

                #region 3. ref 字段和 scoped ref

                {
                    // C# 11开始，开发者可以在 ref struct 中编写 ref 字段，这允许我们将其他对象的引用存储在一个 ref struct 中
                    int x = 1;
                    var foo = new Foo(ref x);
                    foo.X = 2;
                    Console.WriteLine(x); // 2

                    // 这里有一个问题，就是因为 ref field 的存在，可能会使得一个 ref 指向的对象的生命周期被扩展而导致错误，例如：
                    //Foo MyFancyMethod2()
                    //{
                    //    int x = 1;
                    //    var foo = new Foo(ref x);
                    //    return foo; // error
                    //}
                    // 上述代码编译时会报错，因为 foo 引用了局部变量 x，而局部变量 x 在函数返回后生命周期就结束了，
                    // 但是返回 foo 的操作使得 foo 的生命周期比 x 的生命周期更长，这会导致无效引用的问题，因此编译器检测到了这一点，不允许代码通过编译

                    // 但是上述代码中，虽然 foo 确实引用了 x，但是 foo 对象本身并没有长期持有 x 的引用，因为在构造函数后就不再持有对 x 的引用了，
                    // 因此这里按理来说不应该报错。于是C# 11引入了 scoped 的概念，允许开发者显式标注 ref 的生命周期，
                    // 标注了 scoped 的 ref 表示这个引用的生命周期不会超过当前函数的生命周期
                    FooNew MyFancyMethod3()
                    {
                        int x = 1;
                        var foo = new FooNew(ref x);
                        return foo; // error
                    }
                    FooNew fooNew = MyFancyMethod3();

                    // 这样一来，编译器就知道 FooNew2 的构造函数不会使得 FooNew2 在构造函数返回后仍然持有 x 的引用，因此上述代码就能安全通过编译了。
                    // 如果我们试图让一个 scoped ref 逃逸出当前函数的话，编译器就会报错，如此一来，就实现了引用安全。详见：FooNew2


                    // 在字段中，ref 还可以配合 readonly 一起使用，用来表示不可修改的 ref，例如：
                    // 1. ref int：一个 int 的引用
                    // 2. readonly ref int：一个 int 的只读引用（ref是readonly的）
                    // 3. ref readonly int：一个只读 int 的引用（int是readonly的）
                    // 4. readonly ref readonly int：一个只读 int 的只读引用
                    ReadonlyRef readonlyRef = new ReadonlyRef();
                }

                #endregion 3. ref 字段和 scoped ref

                #region 4. 文件局部类型

                {
                    var fileScopeTypes = new FileScopeTypes();
                    fileScopeTypes.Show();

                    // file关键字修饰的类，只能在此类的文件内部可以使用
                    // var foo = new FooFile();
                    // var bar = new BarFile();
                }

                #endregion 4. 文件局部类型

                #region 5. required 成员

                {
                    // Error：未初始化 required 成员，即使使用普通构造函数对 required 成员进行赋值，也无法通过编译
                    // 需要给构造函数加上 SetsRequiredMembers attribute才可以通过编译
                    //var required = new RequiredFoo();

                    // OK
                    var required = new RequiredFoo { X = 1 };

                    // OK
                    var point = new PointRequiredMembers(1, 2);
                }

                #endregion 5. required 成员

                #region 6. checked 运算符

                {
                    // C# 一直都有 checked 和 unchecked 语句，分别表示检查和不检查算法溢出
                    // byte的取值范围：0-255
                    byte x = 100;
                    byte y = 200;

                    // 默认为 unchecked
                    byte z1 = (byte)(x + y);

                    unchecked
                    {
                        // OK
                        byte z = (byte)(x + y);
                    }
                    checked
                    {
                        // Error，抛出异常
                        // byte z = (byte)(x + y);
                    }

                    // 在C# 11中，引入了 checked 运算符，允许用户分别实现用于 checked 和 unchecked 的运算符
                    {
                        var foo1 = new CheckFoo();
                        var foo2 = new CheckFoo();
                        // 调用 operator +
                        var foo3 = unchecked(foo1 + foo2);
                        // 调用 operator checked +
                        var foo4 = checked(foo1 + foo2);
                    }
                }

                #endregion 6. checked 运算符

                #region 7. IntPtr、UIntPtr支持数值运算

                // 在C# 11中，IntPtr 和 UIntPtr 都支持数值运算了，这极大的方便了我们对指针的操作
                {
                    UIntPtr addr = 0x80000048;
                    IntPtr offset = 0x00000016;
                    UIntPtr newAddr = addr + (UIntPtr)offset;
                }

                // 如同 Int32 和 int、Int64 和 long 的关系一样，C#中同样存在 IntPtr 和 UIntPtr 的等价简写，分别为 nint 和 nuint ，n表示 native，
                // 用来表示这个数值的位数和当前运行环境的内存地址位数相同
                {
                    nuint addr = 0x80000048;
                    nint offset = 0x00000016;
                    nuint newAddr = addr + (nuint)offset;
                }

                #endregion 7. IntPtr、UIntPtr支持数值运算

                #region 8. 列表模式匹配

                // C# 11 中新增了列表模式，允许我们对列表进行匹配。
                // 在列表模式中，可以用 [] 来包裹模式，用 _ 代指一个元素，用 .. 代表 0 个或多个元素。
                // 在 .. 后可以声明一个变量，用来创建匹配的子列表，其中包含 .. 所匹配的元素
                {
                    var array = new int[] { 1, 2, 3, 4, 5 };
                    if (array is [1, 2, 3, 4, 5])
                    {
                        Console.WriteLine(1);
                    }

                    if (array is [1, 2, 3, ..])
                    {
                        Console.WriteLine(2);
                    }

                    if (array is [1, _, 3, _, 5])
                    {
                        Console.WriteLine(3);
                    }

                    if (array is [.. var heading, _, 5])
                    {
                        Console.WriteLine(4);
                        Console.WriteLine(heading[0]);
                    }

                    if (array is [1, 2, 3, .. var remaining])
                    {
                        Console.WriteLine(remaining[0]);
                        Console.WriteLine(remaining.Length);
                    }
                }

                // 与其它模式一样，列表模式同样也支持递归，因此可以将列表模式与其它模式组合起来使用
                {
                    var array = new string[] { "hello", ",", "world", "~" };
                    if (array is ["hello", _, { Length: 5 }, { Length: 1 } elem, ..])
                    {
                        Console.WriteLine(elem);
                    }
                }

                // 除了在 if 中使用模式匹配之外，在 switch 中也同样能使用
                {
                    var array = new string[] { "hello", ",", "world", "!" };
                    switch (array)
                    {
                        case ["hello", _, { Length: 5 }, { Length: 1 } elem, ..]:
                            // do something...
                            break;
                        default:
                            break;

                    }

                    // switch 表达式
                    var value = array switch
                    {
                        ["hello", _, { Length: 5 }, { Length: 1 } elem, ..] => 1,
                        _ => 2
                    };
                    Console.WriteLine(value);
                }

                #endregion 8. 列表模式匹配

                #region 9. 对 Span<Char> 的模式匹配

                // 在C#中，Span<char> 和 ReadOnlySpan<char> 都可以看作是字符串的切片，因此C# 11也为这两个类型添加了字符串模式匹配的支持
                {
                    int Foo(ReadOnlySpan<char> span)
                    {
                        if (span is "abcdefg")
                        {
                            return 1;
                        }

                        return 2;
                    }

                    Console.WriteLine(Foo("abcdefg".AsSpan()));
                    Console.WriteLine(Foo("test".AsSpan()));
                }

                #endregion 9. 对 Span<Char> 的模式匹配

                #region 10. 原始字符串

                // C# 原本就有 @ 用来表示不需要转义的字符串，但是用户需要将 " 写成 "" 才能在字符串中包含双引号
                // C# 11引入了原始字符串特性，允许用户利用原始字符串在代码中插入大量的无需转义的文本，方便开发人员在代码中以字符串的方式塞入代码文本等

                // 原始字符串需要至少被三个 " 包裹，例如 """ 或 """"" 等等，前后的引号数量要相等。
                // 另外，原始字符串的缩进由后面引号的位置来确定
                {
                    string str = """
                        hello
                        world
                        """;

                    Console.WriteLine(str);
                }

                {
                    string str = """"
                        hello
                            world
                        """";

                    Console.WriteLine(str);
                }

                {
                    // 可以直接定义JSON数据
                    string json = """
                        {
                            "a": 1,
                            "b": {
                                "c": "hello",
                                "d": "world"
                            },
                            "c": [1, 2 ,3, 4, 5]
                        }
                        """;

                    Console.WriteLine(json);
                    object? obj = JsonConvert.DeserializeObject(json);
                    Hashtable? tb = JsonConvert.DeserializeObject<Hashtable>(json);
                }

                {
                    // 模板字符串
                    int age = 37;
                    string? jsonResult = $$"""
                        {
                            "id": 123,
                            "name": "King",
                            "age": {{age}}
                        }
                        """;
                    Console.WriteLine(jsonResult);
                }

                #endregion 10. 原始字符串

                #region 11. UTF-8 字符串

                {
                    // C# 11引入了UTF-8字符串，可以用 u8 后缀来创建一个 ReadOnlySpan<byte>， 其中包含一个UTF-8字符串
                    {
                        ReadOnlySpan<byte> str1 = "Hello, world"u8;
                        byte[] str2 = "Hello, world"u8.ToArray();
                    }

                    // UTF-8 对于 Web 场景非常有用，因为在 HTTP 协议中，默认编码是 UTF-8，而.NET默认是 UTF-16 编码，
                    // 因此在处理 HTTP 协议时，如果没有 UTF-8 字符串，则会导致大量的 UTF-8 和 UTF-16 字符串相互转换，从而影响性能。
                    // 有了 UTF-8 字符串后，就能非常方便的创建 UTF-8 字面量来使用了，不再需要手动分配一个 byte[] 然后在里面硬编码需要的字符
                }

                #endregion 11. UTF-8 字符串

                #region 12. 模板字符串插值允许换行

                {
                    object group = new Object();

                    string str = $"hello, the object anem is {group
                        .GetType()
                        .FullName}.";
                    Console.WriteLine(str);
                }

                #endregion 12. 模板字符串插值允许换行

                #region 13. struct 自动初始化

                {
                    PointStruct point = new PointStruct(123);
                    Console.WriteLine(point.X);
                    Console.WriteLine(point.Y);
                }

                #endregion 13. struct 自动初始化

                #region 14. 支持对其它参数名进行 nameof

                {
                    // C# 11允许了开发人员在参数中对其它参数名进行 nameof，
                    // 例如在使用 CallerArgumentExpression 这一 attribute 时，此前需要直接硬编码相应参数名的字符串，而现在只需要使用 nameof 即可：
                    void Assert(bool condition, [CallerArgumentExpression(nameof(condition))] string expression = "")
                    {
                        // ...
                    }
                }

                #endregion 14. 支持对其它参数名进行 nameof

                #region 15. 自动缓存静态方法的委托

                {
                    // C# 11开始，从静态方法创建的委托将会被自动缓存
                    void Foo()
                    {
                        Call(Console.WriteLine);
                    }

                    void Call(Action action)
                    {
                        action();
                    }

                    // 此前，每执行一次 Foo，就会从 Console.WriteLine 方法创建一个新的委托，因此如果大量执行 Foo，则会导致大量的委托被重复创建，导致大量的内存被分配，效率极其低下。从 C# 11 开始，将会自动缓存静态方法的委托，因此无论 Foo 被执行多少次，Console.WriteLine 的委托只会被创建一次，节省了内存的同时大幅提升了性能
                }

                #endregion 15. 自动缓存静态方法的委托
            }
            catch
            {
                throw;
            }
        }
    }
}